//
//  ViewController.m
//  RunTime
//
//  Created by jayce on 17/9/25.
//  Copyright © 2017年 jjkj. All rights reserved.
//

#import "ViewController.h"
#import <objc/runtime.h>
#import "Person.h"
#import "Person+AddVariable.h"

@interface ViewController ()

@end

@implementation ViewController {
    
    Person *per;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    per = [[Person alloc] init];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*获取person所有的成员变量*/
- (IBAction)getAllVariable:(id)sender {
    
    unsigned int count = 0;
    //获取类的一个包含所有变量的列表，IVar是runtimei声明的一个宏，是实例变量的意思
    Ivar *allVariables = class_copyIvarList([Person class], &count);
    
    for (int i=0; i<count; i++) {
        
        //遍历每一个变量，包括名称和类型（此处没有型号“*”）
        Ivar ivar = allVariables[i];
        const char *variableName = ivar_getName(ivar);//获取成员变量名称
        const char *variableType = ivar_getTypeEncoding(ivar);//获取成员变量类型
        NSLog(@"(Name:%s)----(Type:%s)",variableName,variableType);
    }
}

/*2.获取person所有方法*/
- (IBAction)getAllMethod:(id)sender {
    
    unsigned int count;
    //获取方法列表，所有在.m文件显式实现的方法都会被找到，包括setter+getter方法；
    Method *allMethods = class_copyMethodList([Person class], &count);
    
    for (int i=0; i<count; i++) {
        
        //Method，为runtime声明的一个宏，表示对一个方法的描述
        Method method = allMethods[i];
        //获取SEL：SEL类型,即获取方法选择器@selector()
        SEL sel = method_getName(method);
        //得到sel的方法名：以字符串格式获取sel的name，也即@selector()中的方法名称
        const char *methodName = sel_getName(sel);
        NSLog(@"(Method:%s)",methodName);
    }
}

/*3.改变person的name变量属性*/
- (IBAction)changeVariable:(id)sender {
    
    NSLog(@"改变前的person：%@",per);
    
    unsigned int count = 0;
    Ivar *allIvarList = class_copyIvarList([Person class], &count);
    Ivar ivar = allIvarList[0];//从第一个例子getAllVariable中输出的控制台信息，我们可以看到name为第一个实例属性。
    object_setIvar(per, ivar, @"Mike");//name属性Tom被强行改为Mike
    
    NSLog(@"改变之后的person：%@",per);
}

/* 4.添加新的属性*/
- (IBAction)addVariable:(id)sender {
    
    per.height = 12;           //给新属性height赋值
    NSLog(@"per.height = %f",[per height]); //访问新属性值
}

/*5.添加新的方法试试(这种方法等价于对Father类添加Category对方法进行扩展)：*/
- (IBAction)addMethod:(id)sender {
    
    /* 动态添加方法：
     第一个参数表示Class cls 类型；
     第二个参数表示待调用的方法名称；
     第三个参数(IMP)myAddingFunction，IMP一个函数指针，这里表示指定具体实现方法myAddingFunction；
     第四个参数表方法的参数，0代表没有参数；
     */
    class_addMethod([per class], @selector(newMethod), (IMP)myAddingFunction, 0);
    //调用方法 【如果使用[per NewMethod]调用方法，在ARC下会报“no visible @interface"错误】
    [per performSelector:@selector(newMethod)];
}

//具体的实现（方法的内部都默认包含两个参数Class类和SEL方法，被称为隐式参数。）
int myAddingFunction(id self, SEL _cmd){
    NSLog(@"已新增方法:NewMethod");
    return 1;

}

/* 6.交换两种方法之后（功能对调），可以试试让苹果乱套... */
- (IBAction)replaceMethod:(id)sender {
    
    NSLog(@"方法交换前执行func1：");
    [per func1];
    
    Method method1 = class_getInstanceMethod([Person class], @selector(func1));
    Method method2 = class_getInstanceMethod([Person class], @selector(func2));
    
    //交换方法
    method_exchangeImplementations(method1, method2);
    NSLog(@"方法交换后执行func1：");
    [per func1]; //输出交换后的效果，需要对比的可以尝试下交换前运行func1；
    
}

- (void)newMethod {
    
    NSLog(@"执行新方法:==newMethod！");
}
@end
